#include "pd.h"


//#define NOT_USE_PRINTF
#define PD_MAX_ACKS 16
#define PD_HEAP_SIZE 16
#define PD_MSG_NEIGH 0
#define PD_MSG_MODEL_DEF 1

#define PD_MSG_MODEL 100
//tag i, i from 100 to MAX_TAGS_TYPE-1+100 means a model message with type in position i-100 in the type vector
#define PD_MSG_TERM_ACK 200
#define PD_MSG_TERM_END 300

#define PD_MODEL_TYPESC 2




int rank, size, v1 = 0, msgc = 0, *destv, active =
  1, terminatedGlobalTermination = 1, sender = 0, result =
  0, resultGlobalTermination = 0, tag_in, tag_out, neighc = 0, parentGlobal =
  0, globalTermination = 0, flush = 0, ackc_in = 0, ackc_out = 0, msgc_out =
  0, type_msg_ord, m, i, fifo_out_order = 1, take_out_heap =
  1, size_of_int, *type_msgv, type_index, termc = 0, msg_end = 0, buffsize =
  0, sent_term = 0, enablPD_to_recv = 0, j, k, flag = 0, l = 0, lmsg =
  0, term = 0, one = 1, end_term=0, matrixIsNull=0; 

long int begin;
void *msgv, *msg;

MPI_Request request;
MPI_Status status;




typedef struct Node
{
  int sender, tag, seq, type_index, **MatrixCausal, lmsg;
  void *msg;

} Node;

//send and receive primitives
int PD_Send (void *msg, int type_index, int dest, int tag_out,
	     int *sendcountv, int MatrixCausal[][size]);
void *PD_Receive (int *sender, int *type_index, int *tag_in, int *recvcountv,
		  int MatrixCausal[][size], Node heap[][PD_HEAP_SIZE],
		  int *heap_count, int *heap_free);

//function to compare two nodes from heap
int PD_Compare_node_fifo (const void *a, const void *b);


int PD_Compare_node_causal(const void *a, const void *b);


int
main (int argc, char *argv[])
{

  MPI_Init (&argc, &argv);

  MPI_Comm_rank (MPI_COMM_WORLD, &rank);
  MPI_Comm_size (MPI_COMM_WORLD, &size);

  int size_of_int = sizeof (int);
  typescount = PD_MODEL_TYPESC;
  size_elem_typev[0] = size_of_int;
  tag_typev[0] = MPI_INT;
  tag_typev[1] = MPI_PACKED;
  int neighv[size], ackv[size], sendcountv[size], recvcountv[size],
    heap_count[size], heap_free[size], MatrixCausal[size][size];
  Node heap[size][PD_HEAP_SIZE];

#ifndef NOT_USE_PRINTF
  printf ("MODEL MSG: I am process %d of %d\n", rank, size);
#endif

  if (rank == 0)
    {
      FILE *pFile;
      int v2;
      pFile = fopen (argv[1], "r");
      if (pFile == NULL)
	perror ("SYSTEM MSG: I am process %d! Error opening graph file!\n");
      else
	{

	  fscanf (pFile, "%d", &m);
#ifndef NOT_USE_PRINTF
	  printf ("MODEL MSG: Graph has %d vertices and %d edges!\n", size,
		  m);
#endif

	  for (i = 0; i < m; i++)
	    {
	      fscanf (pFile, "%d %d\n", &v1, &v2);
#ifndef NOT_USE_PRINTF
	      printf ("MODEL MSG: %d is neighbor of %d\n", v1, v2);
#endif


	      if (v1 == 0 || v2 == 0)
		{
		  if (v1 == 0)
		    {
		      neighv[neighc++] = v2;

		      MPI_Isend (&v1, 1, MPI_INT, v2, PD_MSG_NEIGH,
				 MPI_COMM_WORLD, &request);

		    }
		  if (v2 == 0)
		    {
		      neighv[neighc++] = v1;
		      MPI_Isend (&v2, 1, MPI_INT, v1, PD_MSG_NEIGH,
				 MPI_COMM_WORLD, &request);

		    }

		}
	      else
		{

		  MPI_Isend (&v1, 1, MPI_INT, v2, PD_MSG_NEIGH,
			     MPI_COMM_WORLD, &request);
		  MPI_Isend (&v2, 1, MPI_INT, v1, PD_MSG_NEIGH,
			     MPI_COMM_WORLD, &request);
		}

	    }
	  v1 = -1;

	  globalTermination = atoi (argv[2]);
	  type_msg_ord = atoi (argv[3]);

	  for (i = 1; i < size; i++)
	    {

	      MPI_Isend (&v1, 1, MPI_INT, i, PD_MSG_NEIGH, MPI_COMM_WORLD,
			 &request);
// Nodededede 0 sends to all Nodedededes the characteristics of the model
//global termination

	      MPI_Isend (&globalTermination, 1, MPI_INT, i, PD_MSG_MODEL_DEF,
			 MPI_COMM_WORLD, &request);
	      MPI_Isend (&type_msg_ord, 1, MPI_INT, i, PD_MSG_MODEL_DEF,
			 MPI_COMM_WORLD, &request);


	    }
	}

      fclose (pFile);

    }
  else
    {

      while (v1 != -1)
	{
	  MPI_Recv (&v1, 1, MPI_INT, 0, PD_MSG_NEIGH, MPI_COMM_WORLD,
		    &status);

	  if (v1 != -1)

	    neighv[neighc++] = v1;


	}
      MPI_Recv (&globalTermination, 1, MPI_INT, 0, PD_MSG_MODEL_DEF,
		MPI_COMM_WORLD, &status);
      MPI_Recv (&type_msg_ord, 1, MPI_INT, 0, PD_MSG_MODEL_DEF,
		MPI_COMM_WORLD, &status);

    }

  if (globalTermination == 1)
    {
      terminatedGlobalTermination = 0;
    }


  for (i = 0; i < size; i++) {
	ackv[i] = 0;
	sendcountv[i] = 0;
	recvcountv[i] = 0;
	heap_count[i] = 0;
	heap_free[i] = 0;
	heap[i][0].seq=0;
	if(type_msg_ord==2){
	heap[i][0].MatrixCausal=NULL;
	for(j=0;j<size;j++){
		
			MatrixCausal[i][j]=0;

		}

	}
    }

  PD_Init (argc, argv, rank, neighc, neighv);



begin=clock();


  active = PD_Pulse (0, &msgc, &destv, &msgv, &type_msgv);

  msgc_out += msgc;

#ifndef NOT_USE_PRINTF
  printf
    ("MODEL MSG: I am %d and I've processed PULSE(1) %d. Active: %d. %d messages generated!\n",
     rank, 0, active, msgc);
#endif




  for (i = 0; i < msgc; i++)
    {
#ifndef NOT_USE_PRINTF
      printf
	("MODEL MSG: I am %d and I'm sending message number %d to %d with type index %d!\n",
	 rank, i, destv[i], type_msgv[i]);
#endif
      PD_Send (msgv + i * size_elem_typev[type_msgv[i]], type_msgv[i],
	       destv[i], PD_MSG_MODEL, sendcountv, MatrixCausal);

    }





  if (rank == 0 && globalTermination == 1)
    {
      parentGlobal = -1;
      msgc_out+=size-1;	

    }

  while (((globalTermination == 1) && (termc < neighc))
	 || ((globalTermination == 0) && (active == 1)))
    {



      flag = 0;
      MPI_Iprobe (MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag,
		  &status);

      while (flag==1)
	{


	  msg =
	    PD_Receive (&sender, &type_index, &tag_in, recvcountv,
			MatrixCausal, heap, heap_count, heap_free);



	


	  if (tag_in == PD_MSG_TERM_ACK)
	    {


	      msgc_out -= *(int *) msg;

#ifndef NOT_USE_PRINTF
	      printf
		("MODEL MSG: I am %d and I've received an ACK message %d from %d with tag %d. Deficit: %d!\n",
		 rank, *(int *) msg, sender, tag_in, msgc_out);
#endif

	    }
	  else
	   if (tag_in == PD_MSG_TERM_END)
	    {

	      termc++;
#ifndef NOT_USE_PRINTF
	      printf
		("MODEL MSG: I am %d and I've received an END message from %d. I have termc = %d!\n",
		 rank, sender, termc);
#endif


	      if (sent_term == 0)
		{
		  sent_term = 1;
		  for (i = 0; i < neighc; i++)
		    {

#ifndef NOT_USE_PRINTF
		      printf
			("MODEL MSG: I am %d and I am sending END message %d of size %d to node %d with tag %d\n",
			 rank, msg_end, 1, neighv[i], PD_MSG_TERM_END);
#endif

		      PD_Send (&msg_end, 0, neighv[i], PD_MSG_TERM_END,
			       sendcountv, MatrixCausal);

		    }


		}


	    }


	  else
	    {

	      //computational message

	      if (globalTermination == 1)
		{


		      if (active == 1)
			{
			  ackv[sender]++;

#ifndef NOT_USE_PRINTF
			  printf
			    ("MODEL MSG: I am %d and I'm storing ack to %d. %d acks to him!\n",
			     rank, sender, ackv[sender]);


#endif


			}
			else{	
			#ifndef NOT_USE_PRINTF
		      printf
			("MODEL MSG: I am %d and I am INACTIVE. I am sending an ACK immediately to node %d with tag %d\n",
			 rank, sender, PD_MSG_TERM_ACK);
			#endif
		      PD_Send (&one, 0, sender, PD_MSG_TERM_ACK,
			       sendcountv, MatrixCausal);
		     

			
			}
		    

	


		  if (ackv[sender] >= PD_MAX_ACKS)
		    {

#ifndef NOT_USE_PRINTF
		      printf
			("MODEL MSG: I am %d and I am sending ACK message %d of size %d to node %d with tag %d\n",
			 rank, ackv[sender], 1, sender, PD_MSG_TERM_ACK);
#endif
		      PD_Send (&ackv[sender], 0, sender, PD_MSG_TERM_ACK,
			       sendcountv, MatrixCausal);
		      ackv[sender] = 0;

		    }


		}




#ifndef NOT_USE_PRINTF
	      printf
		("MODEL MSG: I am %d and I've received a message from %d with pulse number %d and type index %d!\n",
		 rank, sender, lmsg, type_index);
#endif

	    



	  while (lmsg > l)
	    {
	      l++;

	      active = PD_Pulse (l, &msgc, &destv, &msgv, &type_msgv);
	
#ifndef NOT_USE_PRINTF
	    printf
		("MODEL MSG: I am %d and I've processed PULSE(2) %d. Active: %d. %d messages generated!\n",
		 rank, l, active, msgc);
#endif
	      msgc_out += msgc;


	      for (i = 0; i < msgc; i++)
		{
#ifndef NOT_USE_PRINTF
		  printf
		    ("MODEL MSG: I am %d and I'm sending message number %d to %d with type index %d!\n",
		     rank, i, destv[i], type_msgv[i]);
#endif
		  PD_Send (msgv + i * size_elem_typev[type_msgv[i]],
			   type_msgv[i], destv[i], PD_MSG_MODEL, sendcountv,
			   MatrixCausal);

		}
	      msgc = 0;


	    }
	 
	
	 PD_Event (msg, type_index, sender);
	}
  	MPI_Iprobe (MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag,
		      &status);
	}

     if(active==1){ 
      l++;
      active = PD_Pulse (l, &msgc, &destv, &msgv, &type_msgv);

      msgc_out += msgc;

#ifndef NOT_USE_PRINTF
      printf
	("MODEL MSG: I am %d and I've processed PULSE(3) %d. Active: %d. %d messages generated!\n",
	 rank, l, active, msgc);
#endif

      for (i = 0; i < msgc; i++)
	{
#ifndef NOT_USE_PRINTF
	  printf
	    ("MODEL MSG: I am %d and I'm sending message number %d to %d with type index %d!\n",
	     rank, i, destv[i], type_msgv[i]);
#endif
	  PD_Send (msgv + i * size_elem_typev[type_msgv[i]], type_msgv[i],
		   destv[i], PD_MSG_MODEL, sendcountv, MatrixCausal);

	}
      msgc = 0;
      }	else{
#ifndef NOT_USE_PRINTF
	  printf ("MODEL MSG: I am %d and I'm gonna flush ack messages\n",
		  rank);
#endif
	  for (i = 0; i < size; i++)
	    {
	      if (ackv[i] > 0 && i != rank)
		{
#ifndef NOT_USE_PRINTF
		  printf
		    ("MODEL MSG: I am %d and I am sending ACK FLUSH message %d of size %d to node %d with tag %d\n",
		     rank, ackv[i], 1, i, PD_MSG_TERM_ACK);
#endif
		  PD_Send (&ackv[i], 0, i, PD_MSG_TERM_ACK, sendcountv,
			   MatrixCausal);
		  ackv[i] = 0;
		}
	    }



	}

#ifndef NOT_USE_PRINTF
printf("MODEL MSG: rank %d msgc_out %d active %d term_end %d\n", rank, msgc_out, active, end_term );
#endif
      if (globalTermination == 1 && msgc_out == 0 && active == 0 && end_term==0)
	{
	end_term=1;



	  
if(parentGlobal!=-1){

	      ackv[parentGlobal]++;
#ifndef NOT_USE_PRINTF
	      printf
		("MODEL MSG: I am %d and I am sending ACK message %d TO PARENT %d of size %d with tag %d\n",
		 rank, ackv[parentGlobal], parentGlobal, 1, PD_MSG_TERM_ACK);
#endif
	      PD_Send (&ackv[parentGlobal], 0, parentGlobal, PD_MSG_TERM_ACK,
		       sendcountv, MatrixCausal);

	      ackv[parentGlobal] = 0;
	


}	else   

	  if (sent_term==0)
	    {
	      sent_term = 1;
	      for (i = 0; i < neighc; i++)
		{

#ifndef NOT_USE_PRINTF
		  printf
		    ("MODEL MSG: I am %d and I am sending END message %d of size %d to node %d with tag %d\n",
		     rank, msg_end, 1, neighv[i], PD_MSG_TERM_END);
#endif

		  PD_Send (&msg_end, 0, neighv[i], PD_MSG_TERM_END,
			   sendcountv, MatrixCausal);
		}

	    }





	}

    }



//time(&end);
if(rank==0){
printf("EXECUTION TIME(milliseconds): %ld\n", (clock()-begin)/CLOCKS_PER_SEC);
}
  PD_Finalize ();

  MPI_Finalize ();
  return 0;


}


int
PD_Send (void *msg, int type_index, int dest, int tag_out, int *sendcountv,
	 int MatrixCausal[][size])
{
#ifndef NOT_USE_PRINTF
      printf ("MODEL MSG: attaching pulse number %d to message\n", l);
#endif

  tag_out += type_index;

  if (type_msg_ord == 0)
    {

      //Node ordering in the messages
      int position, k1, k2, k;



      //k1 = PD_Calculate_buffer_pack_size(1, MPI_INT);
      MPI_Pack_size (1, MPI_INT, MPI_COMM_WORLD, &k1);
      //k2 = PD_Calculate_buffer_pack_size(msglen_send, tag_typev[type_index]);
      MPI_Pack_size (1, tag_typev[type_index], MPI_COMM_WORLD, &k2);

      k = k1 + k2;
      char buff[k];


      position = 0;


      MPI_Pack (&l, 1, MPI_INT, buff, k, &position, MPI_COMM_WORLD);

      MPI_Pack (msg, 1, tag_typev[type_index], buff, k, &position,
		MPI_COMM_WORLD);


      MPI_Send (buff, position, MPI_PACKED, dest, tag_out, MPI_COMM_WORLD);


    }
  else
   if (type_msg_ord == 1)
    {

//fifo ordering
      int position, k1, k2, k;



      //k1 = PD_Calculate_buffer_pack_size(1, MPI_INT);
      MPI_Pack_size (1, MPI_INT, MPI_COMM_WORLD, &k1);
      //k2 = PD_Calculate_buffer_pack_size(msglen_send, tag_typev[type_index]);
      MPI_Pack_size (1, tag_typev[type_index], MPI_COMM_WORLD, &k2);

      k = 2 * k1 + k2;
      char buff[k];


      position = 0;

      int seq;


      sendcountv[dest]++;

      seq = sendcountv[dest];

      //changing seq of messages in order to impose a fifo violation
/*if(rank==0){
      if (seq == 2) {
         seq = 4;
         } else if (seq == 4) {
         seq = 2;
         } 
}*/
      //msgl = msglen_send;

      MPI_Pack (&l, 1, MPI_INT, buff, k, &position, MPI_COMM_WORLD);

      MPI_Pack (&seq, 1, MPI_INT, buff, k, &position, MPI_COMM_WORLD);
      //PD_Add_item_to_pack(buff, k, &position, 1, &seq, MPI_INT);
      //PD_Add_item_to_pack(buff, k, &position, 1, &msgl, MPI_INT);
      //PD_Add_item_to_pack(buff, k, &position, msglen_send, msg, tag_typev[type_index]);

      //MPI_Pack(&msgl, 1, MPI_INT, buff, k, &position, MPI_COMM_WORLD);

      MPI_Pack (msg, 1, tag_typev[type_index], buff, k, &position,
		MPI_COMM_WORLD);


      MPI_Send (buff, position, MPI_PACKED, dest, tag_out, MPI_COMM_WORLD);


    }

  else
   if (type_msg_ord == 2)
    {

	//causal ordering
int k,k1,k2,k3,position;

	



//changing seq of messages in order to impose a fifo violation

	


MatrixCausal[rank][dest]=MatrixCausal[rank][dest]+1;

/*if(rank==0){
if (MatrixCausal[rank][dest] == 2) {
	    MatrixCausal[rank][dest] = 4;
	} else if (MatrixCausal[rank][dest] == 4) {
	    MatrixCausal[rank][dest]  = 2;
	}

}*/

MPI_Pack_size(size*size, MPI_INT, MPI_COMM_WORLD, &k1);
MPI_Pack_size(1, tag_typev[type_index], MPI_COMM_WORLD, &k2);	
 MPI_Pack_size (1, MPI_INT, MPI_COMM_WORLD, &k3);
	

k =  k1 + k2+k3;
	char buff[k];


	position = 0;

	
/*	for (i = 0; i < size; i++) {
	
	
	for(j=0;j<size;j++){
		
			printf("rank %d. causal %d %d %d\n", rank, i, j, MatrixCausal[i][j]);

		}

	
    }

*/	
	MPI_Pack (&l, 1, MPI_INT, buff, k, &position, MPI_COMM_WORLD);

	MPI_Pack(MatrixCausal, size*size, MPI_INT, buff, k, &position, MPI_COMM_WORLD);
	
	MPI_Pack(msg, 1, tag_typev[type_index], buff, k, &position, MPI_COMM_WORLD);


	MPI_Send(buff, position, MPI_PACKED, dest, tag_out, MPI_COMM_WORLD);


/*if(rank==0){
if (MatrixCausal[rank][dest] == 4) {
	    MatrixCausal[rank][dest] = 2;
	} else if (MatrixCausal[rank][dest] == 2) {
	    MatrixCausal[rank][dest]  = 4;
	}

}*/


    }

  else
   if (type_msg_ord == 3)
    {

      //causal relaxed ordering




    }



}

void *
PD_Receive (int *sender, int *type_index, int *tag_in, int *recvcountv,
	    int MatrixCausal[][size], Node heap[][PD_HEAP_SIZE],
	    int *heap_count, int *heap_free)
{


  void *msg;
  int seq;



  if (type_msg_ord == 0)
    {

      //Node ordering in the messages

      //MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
      *sender = status.MPI_SOURCE;

      *tag_in = status.MPI_TAG;




      if (*tag_in >= 100 && *tag_in < 200)
	{
	  //a model message
	  *type_index = *tag_in - 100;
	  *tag_in = 100;
	}
      else if (*tag_in >= 200 && *tag_in < 300)
	{
	  *type_index = *tag_in - 200;
	  *tag_in = 200;
	}
      else if (*tag_in >= 300 && *tag_in < 400)
	{
	  *type_index = *tag_in - 300;
	  *tag_in = 300;
	}
//      #ifndef NOT_USE_PRINTF 
//      printf("TAG %d! Index type %d\n", *tag_in, *type_index);

//      #endif

      //MPI_Get_elements(&status, tag_typev[*type_index], msglen_recv);


      MPI_Get_count (&status, MPI_PACKED, &buffsize);
#ifndef NOT_USE_PRINTF
      printf ("MODEL MSG: get count packed message %d \n", buffsize);
#endif



      char buff[buffsize];


      int position = 0;





      MPI_Recv (buff, buffsize, MPI_PACKED, status.MPI_SOURCE, MPI_ANY_TAG,
		MPI_COMM_WORLD, &status);


      //PD_Unpack_item_from_pack(buff, *msglen_recv, &position, 1, &seq, MPI_INT);
      //PD_Unpack_item_from_pack(buff, *msglen_recv, &position, 1, &msgl, MPI_INT);
      MPI_Unpack (buff, buffsize, &position, &lmsg, 1, MPI_INT,
		  MPI_COMM_WORLD);
#ifndef NOT_USE_PRINTF
      printf ("MODEL MSG: received message pulse number: %d\n", lmsg);
#endif



      PD_Msg_buff_allocate (size_elem_typev[*type_index], *sender, &msg);

      //msg = malloc(msgl * (size_elem_typev[*type_index]));
      //PD_Unpack_item_from_pack(buff, *msglen_recv, &position, msgl, msg, tag_typev[*type_index]);
      MPI_Unpack (buff, buffsize, &position, msg, 1, tag_typev[*type_index],
		  MPI_COMM_WORLD);






    }
  else
   if (type_msg_ord == 1)
    {

      //fifo ordering



      fifo_out_order = 1;
      take_out_heap = 0;

      for (i = 0; i < size; i++)
	{

	  if (heap[i][0].seq == recvcountv[i] + 1 && i != rank)
	    {
	      //can take out element 0 from node i from heap
	      take_out_heap = 1;
	      *sender = i;
	      recvcountv[*sender]++;


	      PD_Heap_remove (heap[*sender], heap_count[*sender],
			      sizeof (Node), PD_Compare_node_fifo);

	      heap_count[*sender]--;

	      msg = heap[*sender][heap_count[*sender]].msg;
	      *sender = heap[*sender][heap_count[*sender]].sender;
	      *tag_in = heap[*sender][heap_count[*sender]].tag;
	      seq = heap[*sender][heap_count[*sender]].seq;
	      lmsg = heap[*sender][heap_count[*sender]].lmsg;
	      *type_index = heap[*sender][heap_count[*sender]].type_index;
#ifndef NOT_USE_PRINTF
	      printf
		("MODEL MSG: I'm taking message out of the HEAP from %d, whose seq is %d!\n",
		 *sender, seq);
#endif


	      break;



	    }

	}


      if (take_out_heap == 0)
	{


	  while (fifo_out_order == 1)
	    {



	      MPI_Probe (MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD,
			 &status);

	      *sender = status.MPI_SOURCE;

	      *tag_in = status.MPI_TAG;

	      if (*tag_in >= 100 && *tag_in < 200)
		{
		  //a model message
		  *type_index = *tag_in - 100;
		  *tag_in = 100;
		}
	      else if (*tag_in >= 200 && *tag_in < 300)
		{
		  *type_index = *tag_in - 200;
		  *tag_in = 200;
		}
	      else if (*tag_in >= 300 && *tag_in < 400)
		{
		  *type_index = *tag_in - 300;
		  *tag_in = 300;
		}



	      MPI_Get_count (&status, MPI_PACKED, &buffsize);
#ifndef NOT_USE_PRINTF
	      printf ("MODEL MSG: get count packed message%d \n", buffsize);
#endif



	      char buff[buffsize];


	      int position = 0;





	      MPI_Recv (buff, buffsize, MPI_PACKED, status.MPI_SOURCE,
			MPI_ANY_TAG, MPI_COMM_WORLD, &status);


	      //PD_Unpack_item_from_pack(buff, *msglen_recv, &position, 1, &seq, MPI_INT);
	      //PD_Unpack_item_from_pack(buff, *msglen_recv, &position, 1, &msgl, MPI_INT);

	      MPI_Unpack (buff, buffsize, &position, &lmsg, 1, MPI_INT,
			  MPI_COMM_WORLD);
#ifndef NOT_USE_PRINTF
	      printf ("MODEL MSG: received message pulse number: %d\n", lmsg);
#endif


	      MPI_Unpack (buff, buffsize, &position, &seq, 1, MPI_INT,
			  MPI_COMM_WORLD);
#ifndef NOT_USE_PRINTF
	      printf ("MODEL MSG: fifo message => seq %d\n", seq);
#endif



	      PD_Msg_buff_allocate (size_elem_typev[*type_index], *sender,
				    &msg);

	      //msg = malloc(msgl * (size_elem_typev[*type_index]));
	      //PD_Unpack_item_from_pack(buff, *msglen_recv, &position, msgl, msg, tag_typev[*type_index]);
	      MPI_Unpack (buff, buffsize, &position, msg, 1,
			  tag_typev[*type_index], MPI_COMM_WORLD);



	      if (seq != recvcountv[*sender] + 1)
		{

#ifndef NOT_USE_PRINTF
		  printf
		    ("MODEL MSG: I am %d! Msg OUT OF ORDER: %d and last received was %d. I'll have to postpone it!\n",
		     rank, seq, recvcountv[*sender]);
#endif




		  heap[*sender][heap_free[*sender]].msg = msg;
		  heap[*sender][heap_free[*sender]].sender = *sender;
		  heap[*sender][heap_free[*sender]].tag = *tag_in;
		  heap[*sender][heap_free[*sender]].seq = seq;
		  heap[*sender][heap_free[*sender]].lmsg = lmsg;
		  heap[*sender][heap_free[*sender]].type_index = *type_index;
		  heap_count[*sender]++;
		  PD_Heap_insert (heap, heap_free[*sender],
				  heap_count[*sender] - 1,
				  heap_count[*sender], sizeof (Node),
				  PD_Compare_node_fifo);
		  heap_free[*sender]++;



		}
	      else
		{
		  recvcountv[*sender]++;
#ifndef NOT_USE_PRINTF
		  printf
		    ("MODEL MSG: msg IN ORDER: number %d and last received was %d\n",
		     seq, recvcountv[*sender]);
#endif

		  fifo_out_order = 0;

		}


	    }

	}



    }

  else
   if (type_msg_ord == 2)
    {

	//causal ordering
	int MatrixCausalAux[size][size];
	fifo_out_order = 1;
	take_out_heap = 0;
	
	for (i = 0; i < size; i++) {
	if(i!=rank){
		enablPD_to_recv=0;
		if(heap[i][0].MatrixCausal!=NULL){

		if(heap[i][0].MatrixCausal[i][rank]==MatrixCausal[i][rank] + 1){
				
		enablPD_to_recv=1;
		 #ifndef NOT_USE_PRINTF 
		printf("MODEL MSG: I am %d! Verifying heap: MatrixCausalHeap[%d][%d]= %d == MatrixCausal[%d][%d]= %d + 1!\n", rank, i, rank, heap[i][0].MatrixCausal[i][rank], i, rank, MatrixCausal[i][rank]);
	#endif
		

		}
		}		
		if(enablPD_to_recv == 1){
		for(k=0;k<size;k++){
		if(k!=i){
			if(MatrixCausal[k][rank] < heap[i][0].MatrixCausal[k][rank]){
			enablPD_to_recv=0;
			break;

			}

		}	
		}

		}		

	
	    if (enablPD_to_recv==1) {
		//can take out element 0 from node i from heap

		take_out_heap = 1;
		*sender = i;

		
		
		
		//recvcountv[*sender]++;


		PD_Heap_remove(heap[*sender], heap_count[*sender], sizeof(Node), PD_Compare_node_causal);

		heap_count[*sender]--;

		#ifndef NOT_USE_PRINTF 
		printf("MODEL MSG: I'm %d and I'm taking message out of the HEAP from %d, whose number in matrix is %d!\n", rank, *sender ,heap[*sender][heap_count[*sender]].MatrixCausal[*sender][rank]);
		#endif


		*type_index = heap[*sender][heap_count[*sender]].type_index;
		msg = heap[*sender][heap_count[*sender]].msg;
		*sender = heap[*sender][heap_count[*sender]].sender;
		*tag_in = heap[*sender][heap_count[*sender]].tag;
		lmsg = heap[*sender][heap_count[*sender]].lmsg;
	
		for(k=0;k<size;k++){
				for(j=0;j<size;j++){
					if(heap[*sender][heap_count[*sender]].MatrixCausal[k][j]>MatrixCausal[k][j]){
					MatrixCausal[k][j]=heap[*sender][heap_count[*sender]].MatrixCausal[k][j];
					}	
				}

			}




		//seq = heap[*sender][heap_count[*sender]].seq;
		


		break;



	    }

	}
	}


	if (take_out_heap == 0) {


	    while (fifo_out_order == 1) {



		MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

		*sender = status.MPI_SOURCE;

		*tag_in = status.MPI_TAG;

		if (*tag_in >= 100 && *tag_in < 200) {
		    //a model message
		    *type_index = *tag_in - 100;
		    *tag_in = 100;
		} else if (*tag_in >= 200 && *tag_in < 300) {
		    *type_index = *tag_in - 200;
		    *tag_in = 200;
		} else if (*tag_in >= 300 && *tag_in < 400) {
		    *type_index = *tag_in - 300;
		    *tag_in = 300;
		}
		 
		

		MPI_Get_count(&status, MPI_PACKED, &buffsize);
		#ifndef NOT_USE_PRINTF 
		printf("MODEL MSG: I am %d and get count packed message %d \n", rank, buffsize);
		#endif



		char buff[buffsize];


		int position = 0;

		



		MPI_Recv(buff, buffsize, MPI_PACKED, status.MPI_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

		   MPI_Unpack (buff, buffsize, &position, &lmsg, 1, MPI_INT,
			  MPI_COMM_WORLD);
#ifndef NOT_USE_PRINTF
	      printf ("MODEL MSG: received message pulse number: %d\n", lmsg);
#endif
	
		MPI_Unpack(buff,buffsize, &position, MatrixCausalAux, size*size, MPI_INT, MPI_COMM_WORLD);


		/*for (i = 0; i < size; i++) {
	
	
		for(j=0;j<size;j++){
		
			printf("rank %d. causal aux %d %d %d\n", rank, i, j, MatrixCausalAux[i][j]);
	
			}

	
  		  }*/
	
	        PD_Msg_buff_allocate(size_elem_typev[*type_index], *sender, &msg);
        
		//msg = malloc(msgl * (size_elem_typev[*type_index]));
		//PD_Unpack_item_from_pack(buff, *msglen_recv, &position, msgl, msg, tag_typev[*type_index]);
		MPI_Unpack(buff, buffsize, &position, msg, 1, tag_typev[*type_index], MPI_COMM_WORLD);
		


		enablPD_to_recv=0;
		if(MatrixCausalAux[*sender][rank]==MatrixCausal[*sender][rank] + 1){
		enablPD_to_recv=1;

		}
		if(enablPD_to_recv == 1){
		for(k=0;k<size;k++){
		if(k!=*sender){
			if(MatrixCausal[k][rank] < MatrixCausalAux[k][rank]){
				

			
			enablPD_to_recv=0;
			break;

			}

		}	
		}

		}		



		
		if (enablPD_to_recv==0) {

		    #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d! Msg OUT OF ORDER: MatrixCausal[%d][%d]=%d and MatrixCausalRecv[%d][%d]=%d. I'll have to postpone it!\n", rank, *sender, rank, MatrixCausal[*sender][rank], *sender, rank, MatrixCausalAux[*sender][rank]);
#endif



	
		   


			matrixIsNull=0;
			if(heap[*sender][heap_free[*sender]].MatrixCausal==NULL){
				
		    	matrixIsNull=1;	
			heap[*sender][heap_free[*sender]].MatrixCausal=malloc(size*sizeof(int*));
		
			}
			
		    
			for(i=0;i<size;i++){
				if(matrixIsNull==1){
				heap[*sender][heap_free[*sender]].MatrixCausal[i]=malloc(size*sizeof(int));	
				}
				for(j=0;j<size;j++){
				heap[*sender][heap_free[*sender]].MatrixCausal[i][j] = MatrixCausalAux[i][j];			

				}

			}	

				    
                    heap[*sender][heap_free[*sender]].msg = msg;
		    heap[*sender][heap_free[*sender]].sender = *sender;
		    heap[*sender][heap_free[*sender]].tag = *tag_in;			
		    heap[*sender][heap_free[*sender]].type_index = *type_index;
		    heap[*sender][heap_free[*sender]].lmsg=lmsg;	
		    heap_count[*sender]++;
		    PD_Heap_insert(heap, heap_free[*sender], heap_count[*sender] - 1, heap_count[*sender], sizeof(Node), PD_Compare_node_causal);
		    heap_free[*sender]++;



		} else {
		     #ifndef NOT_USE_PRINTF 
printf("MODEL MSG: I am %d! Msg IN ORDER: MatrixCausal[%d][%d]=%d and MatrixCausalRecv[%d][%d]=%d. I'll receive it!\n", rank, *sender, rank, MatrixCausal[*sender][rank], *sender, rank, MatrixCausalAux[*sender][rank]);
#endif

			for(i=0;i<size;i++){
				for(j=0;j<size;j++){
					if(MatrixCausalAux[i][j]>MatrixCausal[i][j]){
					MatrixCausal[i][j]=MatrixCausalAux[i][j];
					}	
				}

			}


		   // #ifndef NOT_USE_PRINTF 
//printf("MODEL MSG: msg IN ORDER: number %d and last received was %d\n", seq, recvcountv[*sender]);
//#endif

		    fifo_out_order = 0;

		}


	    }

	}







    }

  else
   if (type_msg_ord == 3)
    {

      //causal relaxed ordering
    }



  return msg;


}

int
PD_Compare_node_fifo (const void *a, const void *b)
{
  return ((*(Node *) b).seq - (*(Node *) a).seq);
}


int PD_Compare_node_causal(const void *a, const void *b)
{
    return ((*(Node *) b).MatrixCausal[(*(Node *) b).sender][rank] - (*(Node *) a).MatrixCausal[(*(Node *) a).sender][rank]);
}
